﻿#include "tcpsocket.h"

#include <errno.h>
#include <stdio.h>

#ifdef _WIN32
#  include <winsock2.h>      // 所有 socket 函数
#  include <ws2tcpip.h>      // 扩展：sockaddr_in6, inet_pton, inet_ntop...
#  pragma comment(lib, "ws2_32.lib")
using ssizet = int;       // Windows 没有 ssize_t
#else
#include <sys/time.h>
#  include <sys/socket.h>
#  include <netinet/in.h>
#  include <arpa/inet.h>
#  include <unistd.h>
#  include <sys/types.h>
using ssizet = ssize_t;
#endif


TcpSocket::TcpSocket()
{
}

TcpSocket::TcpSocket(int socket)
{
    mSocket = socket;
    if (mSocket != -1)
    {
        sockaddr_in addr;
        socklen_t len = sizeof(addr);
        auto ret = getpeername(mSocket, (sockaddr *)&addr, &len);
        if (ret == 0)
        {
            mPeerIp = inet_ntoa(addr.sin_addr);
        }
    }
}

TcpSocket::~TcpSocket()
{
    disconnect();
}

bool TcpSocket::connect(const string &ip, int port)
{
    if (mSocket != -1)
        return true;

    mSocket = socket(AF_INET, SOCK_STREAM, 0);
    if (mSocket == -1)
    {
        perror("faield to create socket");
        return false;
    }

    sockaddr_in addr;
    addr.sin_addr.s_addr = inet_addr(ip.c_str());
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    int ret = ::connect(mSocket, (sockaddr *)&addr, sizeof(addr));
    if (ret == -1)
    {
        perror("failed to connect");
  
#ifdef _WIN32
		closesocket(mSocket);
#else
		close(mSocket);
#endif
        return false;
    }

    mPeerIp = ip;
    return true;
}

void TcpSocket::disconnect()
{
    if (mSocket != -1)
    {
#ifdef _WIN32
		closesocket(mSocket);
#else
		close(mSocket);
#endif
        mPeerIp = "";
    }
}

int TcpSocket::send(const void *data, int size)
{
    int sent = 0;
    auto pdata = static_cast<const char *>(data);

    while (sent < size)
    {
        int ret = ::send(mSocket, pdata + sent, size - sent, 0);
        if (ret == -1)
        {
            if (errno != EAGAIN)
            {
                perror("tcp send failed");
                return -1;
            }
            continue;
        }

        sent += ret;
    }

    return sent;
}

int TcpSocket::recv(void *data, int size, int msTimeout)
{

#ifdef _WIN32
	DWORD timeout = msTimeout;  // 单位毫秒
	setsockopt(mSocket, SOL_SOCKET, SO_RCVTIMEO,
		(const char*)&timeout, sizeof(timeout));

    int ret = ::recv(mSocket, static_cast<char*>(data), size, 0);
#else
	timeval tv{ msTimeout / 1000, (msTimeout % 1000) * 1000 };
	setsockopt(mSocket, SOL_SOCKET, SO_RCVTIMEO,
		&tv, sizeof(tv));
	int ret = ::recv(mSocket, data, size, 0);

#endif

    if (ret == -1)
    {
        if (errno == ETIMEDOUT || errno == EAGAIN)
            return -1;
        else
        {
            perror("recv failed");
            return -2;
        }
    }

    return ret;
}
